package org.xi2p;

import java.util.ArrayList;
import java.util.LinkedHashSet;

import static org.xi2p.core.*;
import static org.xi2p.core.printWriter;

public class RespondUserplay extends Respond{
    ArrayList<KeyDownEvent> keyEvents = new ArrayList<>();
    public int pressedKeyNum = 0;
    @Override
    public void respond(String command) {
        String[] blocks = command.split(" ");

        if ("gc".equals(blocks[0])) {
            // get canvas
            // command format: gc beat
            double beat = Double.parseDouble(blocks[1]);
            ArrayList<Note> visibleNotes = new ArrayList<>();
            ArrayList<Note> AllNotes = new ArrayList<>();
            ArrayList<Note> noteBin = new ArrayList<>();
            ArrayList<Evaluation> Eval = new ArrayList<>();
            ArrayList<Note> Audio = new ArrayList<>();

            for (Judgeline judgeline : judgeline_list) {
                judgeline.update(beat);
                visibleNotes.addAll(judgeline.visibleNotes);
                AllNotes.addAll(judgeline.notHolds);
                AllNotes.addAll(judgeline.holds);
            }

            AllNotes.sort(new NoteComparator<>());

            long current = System.currentTimeMillis();

            for (Note note : AllNotes){
                // 删除 & 判定
                // 还远远没有到达判定时间的note不进行判定
                if (beat + KEYBOARD_OFFSET < note.at - 2 * GOOD) continue;

                if (note.type == 1) {
                    // Tap
                    if (beat + KEYBOARD_OFFSET > note.at + GOOD) {
//                        Eval.add(new Evaluation(note, Evaluation.MISS));
                        noteBin.add(note);
                        missNum++;
                        combo = 0;
                    } else if (!keyEvents.isEmpty()){
                        noteBin.add(note);
                        if (beat + KEYBOARD_OFFSET < note.at - GOOD){
                            Eval.add(new Evaluation(note, Evaluation.BAD));
                            note.lastEvalTime = current;
                            badPainter.addBad(note);
                            badNum++;
                            combo = 0;
                        } else if (beat + KEYBOARD_OFFSET < note.at - PERFECT || beat + KEYBOARD_OFFSET > note.at + PERFECT){
                            Eval.add(new Evaluation(note, Evaluation.GOOD));
                            Audio.add(note);
                            goodNum++;
                            combo++;
                        } else {
                            Eval.add(new Evaluation(note, Evaluation.PERFECT));
                            Audio.add(note);
                            perfectNum++;
                            combo++;
                        }
                        keyEvents.remove(0);
                    }
                } else if (note.type == 2 || note.type == 3) {
                    // Drag or Flick
                    if (beat + KEYBOARD_OFFSET > note.at + GOOD) {
//                        Eval.add(new Evaluation(note, Evaluation.MISS));
                        noteBin.add(note);
                        missNum++;
                        combo = 0;
                    } else if (pressedKeyNum != 0){
                        // 有按着就可以
                        if (!keyEvents.isEmpty()){
                            keyEvents.remove(0);
                        }
                        if (note.evaluation == null){
                            // 还没有判定过
                            // flick 和 drag 只有 perfect / miss评级
                            // 只要在其判定区间内有一个时刻键盘按下，就判定其为perfect
                            if (beat + KEYBOARD_OFFSET > note.at - GOOD){
                                note.evaluation = new Evaluation(note, Evaluation.PERFECT);
                            }
                        }
                        if (note.evaluation != null && beat + KEYBOARD_OFFSET > note.at){
                            Eval.add(note.evaluation);
                            perfectNum++;
                            combo++;
                            Audio.add(note);
                            noteBin.add(note);
                        }
                    }
                } else {
                    // Hold
                    if (note.evaluation == null){
                        // hold 的头判定
                        if (beat + KEYBOARD_OFFSET > note.end){
                            noteBin.add(note);
                        }
                        if (beat + KEYBOARD_OFFSET > note.at + GOOD) {
//                            note.evaluation = new Evaluation(note, Evaluation.MISS);
                            note.miss = true;
                            missNum++;
                            combo = 0;

                        } else if (!keyEvents.isEmpty()){
                            keyEvents.remove(0);
                            if (beat + KEYBOARD_OFFSET < note.at - GOOD){
                                // Hold BAD 直接变红消失
                                Eval.add(new Evaluation(note, Evaluation.BAD));
                                note.lastEvalTime = current;
                                badPainter.addBad(note);
                                noteBin.add(note);
                                badNum++;
                                combo = 0;
                            } else if (beat + KEYBOARD_OFFSET < note.at - PERFECT || beat + KEYBOARD_OFFSET > note.at + PERFECT){
                                note.evaluation = new Evaluation(note, Evaluation.GOOD);
                                Eval.add(note.evaluation);
                                note.lastEvalTime = System.currentTimeMillis();
                                Audio.add(note);
                            } else {
                                note.evaluation = new Evaluation(note, Evaluation.PERFECT);
                                Eval.add(note.evaluation);
                                note.lastEvalTime = System.currentTimeMillis();
                                Audio.add(note);
                            }
                        }
                    } else{
                        // 已经进行过头判定
                        // 接下来要确保整个Hold被全时段press

                        if (current - note.lastEvalTime > 200 && !note.miss){
                            note.lastEvalTime = current;
                            Eval.add(note.evaluation);
                        }

                        if (beat + KEYBOARD_OFFSET >= note.end){
                            noteBin.add(note);
                            if (note.evaluation.evalString.equals(Evaluation.GOOD)) {
                                goodNum++;
                            } else {
                                perfectNum++;
                            }
                            combo++;
                            continue;
                        }
                        if (pressedKeyNum == 0){
                            // 没有按着任何键，hold会断
                            note.miss = true;
                            missNum++;
                            combo = 0;

                        }
                    }
                }
            }

            for (Note note : noteBin){
                note.judgeline.above1.remove(note);
                note.judgeline.above2.remove(note);
                note.judgeline.holds.remove(note);
                note.judgeline.notHolds.remove(note);
            }

            for (Judgeline judgeline : judgeline_list) {
                if (judgeline.point1 != null && judgeline.point2 != null) {
                    printWriter.write("%.2f %.2f %.2f %.2f %.2f|".
                            formatted(judgeline.point1[0], judgeline.point1[1],
                                    judgeline.point2[0], judgeline.point2[1],
                                    judgeline.alpha));
//                    printWriter.write("%.2f %.2f|".formatted(judgeline.x, judgeline.y));
                }

            }
            printWriter.write("$");
            printWriter.flush();

            // 标记highlight
            ArrayList<Note> _notes = new ArrayList<>(visibleNotes);
            for (int i = 0; i < _notes.size(); i++) {
                Note noteI = _notes.get(i);
                for (int j = i + 1; j < _notes.size(); j++) {
                    Note noteJ = _notes.get(j);
                    if (noteI.at == noteJ.at) {
                        noteI.highlight = true;
                        noteJ.highlight = true;
                        j--;
                        _notes.remove(noteJ);
                    }
                }
//
            }

            LinkedHashSet<String> strings = new LinkedHashSet<>();
            visibleNotes.sort(new NoteComparator<>());
            for (Note note : visibleNotes) {
                strings.add(note.blit(beat));

            }

            for (String noteString : strings) {
                printWriter.write(noteString);
            }
//                    printWriter.write("%.2f %.2f|".formatted(note.xInSurface, note.yInSurface));

            printWriter.write("$");
            printWriter.flush();

            badPainter.writeAndFlush(current);
            // $

            for (Evaluation evaluation : Eval) {
                String evalString = switch (evaluation.evalString) {
                    case "PERFECT" -> "1";
                    case "GOOD" -> "0";
                    default -> null;
                };
                if (evalString != null){
                    printWriter.write("%.2f %.2f %s|".formatted(
                            evaluation.note.xInSurface,
                            evaluation.note.yInSurface,
                            evalString));
    //                    printWriter.write("%.2f %.2f|".formatted(note.xInSurface, note.yInSurface));
                }
            }

            printWriter.write("$");
            printWriter.flush();



            for (Note note : Audio) {
                printWriter.write("%d|".formatted(note.type));
            }

            printWriter.write("$");
            printWriter.flush();


            if (combo > maxCombo) maxCombo = combo;

            score = (int) (
                    (900000 * (1.0 * perfectNum / noteNum + 0.8 * goodNum / noteNum) + 100000.0 * maxCombo / noteNum)
            );
            printWriter.write("%d|%d|%d|%d|%d|%d|%d|".formatted(
                    score, perfectNum, goodNum, badNum, missNum, combo, maxCombo));
            printWriter.write("$");
            printWriter.flush();
            keyEvents.clear();

        } else if (blocks[0].equals("pk")) {
            // press key
            // command format: pk (1 if down else 0) scancode beat
            double beat = Double.parseDouble(blocks[3]);
            int down = Integer.parseInt(blocks[1]);
            int scancode = Integer.parseInt(blocks[2]);
            if (down == 1){
                pressedKeyNum++;
                keyEvents.add(new KeyDownEvent(scancode, beat));
            } else {
                pressedKeyNum--;
                if (pressedKeyNum < 0) pressedKeyNum=0;
            }

        } else {
            System.out.println("500 INVALID COMMAND HEAD");
        }
    }
}
